package com.scs.application.modules.fsd.socket;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.modules.fsd.entity.FsdStation;
import com.scs.application.modules.fsd.service.StationService;
import org.springframework.beans.factory.annotation.Autowired;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * socket服务端
 * 标注为端点：@ServerEndpoint，其中"/fsd/{stationCode}"为访问路径
 */
@ServerEndpoint("/websocket/{stationCode}")
public class FsdWebSocketEndpoint {
    private static StationService stationService;
    @Autowired
    public void setStationService(StationService stationService){
        FsdWebSocketEndpoint.stationService=stationService;
    }
    /**
     * 存储所有存活的用户
     * 注意1：高并发问题
     * 注意2：livingSessions必须是全局变量（保证全局就他一个变量，否则每次调用都会被刷新）
     * 注意3：很难保证，客户端在退出时，正确调用了WebSocket.close()，也就是调用后端的onClose()方法，这样
     * 就可能会导致Session无法被有效清除，livingSessions会越来越大，服务器压力也会越来越大。
     * 所以，我们需要周期性的去检查用户是否还处于活跃状态，不活跃的，移除该用户的session
     */
    private static Map<String, Session> livingSessions = new ConcurrentHashMap<>();

    /**
     * 前端一旦启用WebSocket,机会调用@OnOpen注解标注的方法
     *
     * @param stationCode 站点
     * @param session  会话，每个访问对象都会有一个单独的会话
     */
    @OnOpen
    public void openSession(@PathParam("stationCode") String stationCode, Session session) {
        livingSessions.put(stationCode, session);
        stationService.update(Wrappers.<FsdStation>update().set("online_status",true).eq("station_code",stationCode));
        sendTextAll("站点【" + stationCode + "】连接成功！");
    }

    /**
     * 服务端发送消息给前端时调用
     *
     * @param stationCode 路径参数
     * @param session  会话，每个访问对象都会有一个单独的会话
     * @param message  待发送的消息
     */
    @OnMessage
    public void onMessage(@PathParam("stationCode") String stationCode, Session session, String message) {
        switch (message){
            case "heartbeat":
                sendText(session,"connect");
                return;
        }
        sendTextAll("站点【" + stationCode + "】：" + message);
    }

    /**
     * 客户端关闭WebSocket连接时，调用标注@OnClose的方法
     *
     * @param stationCode 路径参数
     * @param session  会话，每个访问对象都会有一个单独的会话
     */
    @OnClose
    public void onClose(@PathParam("stationCode") String stationCode, Session session) {
        //将当前用户移除
        livingSessions.remove(stationCode);
        stationService.update(Wrappers.<FsdStation>update().set("online_status",false).eq("station_code",stationCode));
        //给所有存活的用户发送消息
        sendTextAll("站点【" + stationCode + "】断开连接！");
    }
    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        sendText(session,"发生错误");
        error.printStackTrace();
    }


    /**
     * 向指定站点(用户)发送更新指令
     */
    public void sendUpgrade(String stationCode) {
        if (livingSessions.containsKey(stationCode))
            sendText(livingSessions.get(stationCode), "upgrade");
        else
            throw new BusinessException("站点不在线！");
    }

    /**
     * 向指定Session(用户)发送message
     */
    private void sendText(Session session, String message) {
        //发送消息对象
        RemoteEndpoint.Basic basicRemote = session.getBasicRemote();
        try {
            //发送消息
            basicRemote.sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 遍历所有存活的用户，并发送消息（PS：就是广播消息）
     */
    private void sendTextAll(String message) {
        //lambda表达式
        livingSessions.forEach((sessionId, session) -> sendText(session, message));
    }
}
